import { Box3, Euler, MathUtils, Matrix4, Mesh, PerspectiveCamera, Quaternion, Ray, Raycaster, Spherical, Vector2, Vector3, Vector4 } from "three";
import Bounds from "../renderExtension/types/Bounds";
import { App, GeoUtils } from "../Engine";
import Helper from "../utils/Helper";
import Events from "../application/constants/Events";
import { Point } from "../renderExtension/types/Point";
import Const from "../application/constants/Const";
import { config } from "process";
import Config from "../application/constants/Config";
import { off } from "process";
import Picker from "./Picker";
import Pixel from "../renderExtension/types/Pixel";
/** 浏览 */
export default class Orbit {
    public app: App;
    private camera: PerspectiveCamera;
    private zoomSpeed: number;
    private scale: number;
    private offset: Vector3;
    private quat: Quaternion;
    private quatInverse: Quaternion;
    private target: Vector3;
    private autoRotate: boolean;
    private autoRotateSpeed: number;
    public spherical: Spherical;
    public sphericalDelta: Spherical;
    private minPolarAngle: number;//最小倾斜角
    private maxPolarAngle: number;//最大倾斜角
    private minAzimuthAngle: number;
    private maxAzimuthAngle: number;
    private minDistance: number;
    private maxDistance: number;
    private panOffset: Vector3;
    public phi: number; //用于记录地图的俯仰倾斜角
    public theta: number; //用于记录地图的方位角度
    private enableDamping: boolean;
    private dampingFactor: number;
    private rotateStart: Vector2;
    private rotateEnd: Vector2;
    private rotateDelta: Vector2;
    private panStart: Vector2;
    private panEnd: Vector2;
    private rotateSpeed: number;
    private isDragging: boolean;
    private tmpPanOffset: Vector3;
    private tmpSphericalDelta: Spherical;
    private isCameraChanging: boolean;
    private lastCameraPosition: Vector3;
    private lastCameraLookat: Vector3;
    private lastQuaternion: Quaternion;
    public enabled: boolean = true;

    constructor(camera: PerspectiveCamera) {
        this.camera = camera;
        this.app = App.getInstance();
        this.target = this.app.camera.lookAtTarget;
        this.initAttributes();
        this.register();
    }

    protected initAttributes() {
        this.quat = new Quaternion().setFromUnitVectors(this.camera.up, new Vector3(0, 1, 0));
        this.quatInverse = this.quat.clone().conjugate();
        this.zoomSpeed = 1.5;
        this.scale = 1;
        this.offset = new Vector3();
        this.autoRotate = false;
        this.autoRotateSpeed = 1.5;
        this.spherical = new Spherical();
        this.sphericalDelta = new Spherical();
        this.minPolarAngle = 0;
        this.maxPolarAngle = MathUtils.degToRad(30);
        this.minAzimuthAngle = - Infinity;
        this.maxAzimuthAngle = Infinity;
        this.minDistance = this.camera.near;
        this.maxDistance = this.camera.far;
        this.panOffset = new Vector3();
        this.phi = 0;
        this.enableDamping = this.app.options.Map.enableDamping;
        this.dampingFactor = 0.05;
        this.rotateStart = new Vector2();
        this.rotateEnd = new Vector2();
        this.rotateDelta = new Vector2();
        this.panStart = new Vector2();
        this.panEnd = new Vector2();
        this.rotateSpeed = 0.4;
        this.tmpPanOffset = new Vector3();
        this.tmpSphericalDelta = new Spherical();
        this.lastCameraPosition = this.camera.position.clone();
        this.lastCameraLookat = this.app.camera.lookAtTarget.clone();
        this.lastQuaternion = this.camera.quaternion;
        this.isCameraChanging = false;
    }

    private isChangeVertical = false;
    public update() {
        if (this.app.camera.cameraUtils.isCollisionEye) {
            return;
        }

        let position = this.camera.position;
        this.target = this.app.camera.lookAtTarget;
        let oldTargetZ = this.target.z;
        let oldTarget = this.target.clone();
        this.offset.copy(position).sub(this.target);
        this.offset.applyQuaternion(this.quat);
        this.spherical.setFromVector3(this.offset);
        if (this.autoRotate) {
            this.rotateLeft(this.getAutoRotationAngle());
        }
        if (this.enableDamping) {
            this.spherical.theta += this.sphericalDelta.theta * this.dampingFactor;
            this.spherical.phi += this.sphericalDelta.phi * this.dampingFactor;
        } else {
            this.spherical.theta += this.sphericalDelta.theta;
            this.spherical.phi += this.sphericalDelta.phi;
        }

        if (this.app.map.zoom <= this.app.options.Map.verticalLevel) {
            if (this.isChangeVertical == false) {
                this.isChangeVertical = true;
                try {
                    this.app.mouse.setCameraBeforeWheel({ pixel: this.app.mouse.pixelMouse });
                } catch (error) {
                    console.log(error);
                }
            }
            this.setMaxPolarAngle(0);

            this.minAzimuthAngle = 0;
            this.maxAzimuthAngle = 0;
        } else {
            this.setMaxPolarAngle(this.app.options.Map.maxSlopeAngle);

            this.maxAzimuthAngle = Infinity;
            this.minAzimuthAngle = -Infinity;
        }
        this.spherical.phi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.spherical.phi));
        this.spherical.theta = Math.max(this.minAzimuthAngle, Math.min(this.maxAzimuthAngle, this.spherical.theta));

        if (Math.abs(this.phi - this.spherical.phi) > 0.0001 || Math.abs(this.theta - this.spherical.theta) > 0.0001) { //地图的俯仰角或者方位角发生变化
            this.app.fireEvent(Events.AngleChange);
        }

        this.phi = this.spherical.phi;
        this.theta = this.spherical.theta;
        this.spherical.makeSafe();
        this.spherical.radius *= this.scale;
        this.minDistance = this.camera.near;
        this.spherical.radius = Math.max(this.minDistance, Math.min(this.maxDistance, this.spherical.radius));

        this.target.x += this.panOffset.x;
        this.target.y += this.panOffset.y;
        this.target.z += this.panOffset.z;
        this.offset.setFromSpherical(this.spherical);
        this.offset.applyQuaternion(this.quatInverse);
        // this.checkCameraCollision(this.target, this.offset);

        let tempPosition = this.target.clone().add(this.offset);
        if (this.isDragging && this.app.options.Camera.isOpenEyeIntersect) { //拖拽地图的时候，避免相机和场景中的物体发生碰撞
            let adjustMsg = this.app.camera.cameraUtils.adjustCameraPosition(position, oldTarget, tempPosition, this.target);
            tempPosition = adjustMsg.eyePoint;
            this.target = adjustMsg.eyeTarget;
        }

        position.copy(tempPosition);

        this.target.z = oldTargetZ;
        this.app.camera.lookAt(this.target);

        this.tmpPanOffset.copy(this.panOffset);
        this.tmpSphericalDelta.copy(this.sphericalDelta);

        if (this.enableDamping) {
            this.sphericalDelta.theta *= (1 - this.dampingFactor);
            this.sphericalDelta.phi *= (1 - this.dampingFactor);
            this.panOffset.multiplyScalar(1 - this.dampingFactor);
            if (this.isDragging && Helper.vector3Equals(this.panOffset, this.tmpPanOffset) && Helper.sphericalEquals(this.sphericalDelta, this.tmpSphericalDelta)) {
                this.sphericalDelta.set(0, 0, 0);
                this.panOffset.set(0, 0, 0);
                // this.app.fireEvent(Events.MapDragEnd);
                this.app.fireEvent(Events.MapMoveEnd);
                this.isDragging = false;
            }
        } else {
            this.sphericalDelta.set(0, 0, 0);
            this.panOffset.set(0, 0, 0);
            if (this.isDragging && Helper.vector3Equals(this.panOffset, this.tmpPanOffset) && Helper.sphericalEquals(this.sphericalDelta, this.tmpSphericalDelta)) {
                // this.app.fireEvent(Events.MapDragEnd);
                this.app.fireEvent(Events.MapMoveEnd);
                this.isDragging = false;
            }
        }

        this.scale = 1;
        //camera的位置或者视线发生了变化
        if (!Helper.vector3Equals(this.camera.position, this.lastCameraPosition) ||
            !Helper.vector3Equals(this.app.camera.lookAtTarget, this.lastCameraLookat) ||
            !Helper.quaternionEquals(this.camera.quaternion, this.lastQuaternion)) {
            let projectionMatrix = this.camera.projectionMatrix.clone();
            let matrixWorld = this.camera.matrixWorld.clone();
            this.isCameraChanging = true;
            this.app.fireEvent(Events.MapRange);

            this.app.fireEvent(Events.CameraChange, projectionMatrix, matrixWorld);
        } else {
            if (this.isCameraChanging) {
                this.app.fireEvent(Events.CameraChangeEnd);
                this.isCameraChanging = false;
            }
        }
       this.recordCameraMsg();
    }

    private recordCameraMsg(){
        this.lastCameraPosition = this.camera.position.clone();
        this.lastCameraLookat = this.app.camera.lookAtTarget.clone();
        this.lastQuaternion = this.camera.quaternion.clone();
    }

    private limitMapRange(){ //限制地图的范围在特定的范围内，默认在web墨卡托坐标范围内
        let position = this.camera.position; // || (Math.abs(position.x) > Const.Number.MERCATORANGE || Math.abs(position.y) > Const.Number.MERCATORANGE)
        if(!Helper.vector3Equals(this.lastCameraPosition, position)){ //相机位置发生变化    
            let visualBound: Array<Vector3> = this.app.map.computeVisualBound(); //获取地图的可视边界
            let minBoundX = Infinity, maxBoundX = -Infinity, minBoundY = Infinity, maxBoundY = -Infinity;
            try {
                visualBound.forEach((bound) => {
                    minBoundX = Math.min(minBoundX, bound.x);
                    maxBoundX = Math.max(maxBoundX, bound.x);
                    minBoundY = Math.min(minBoundY, bound.y);
                    maxBoundY = Math.max(maxBoundY, bound.y);
                })
            } catch (error) {
                return;
            }

            let mapRange = Config.Map.mapRange;
            if ((maxBoundX > mapRange.maxX && minBoundX < mapRange.minX) || (maxBoundY >= mapRange.maxY && minBoundY < mapRange.minY)) {
                position.copy(this.lastCameraPosition);
                this.target.copy(this.lastCameraLookat);
                return;
            }


            let isCrossRange = false;
            if (maxBoundX > mapRange.maxX) {
                position.x -= maxBoundX - mapRange.maxX;
                this.target.x -= maxBoundX - mapRange.maxX;
                isCrossRange = true;
            }else if(minBoundX < mapRange.minX){
                position.x += mapRange.minX - minBoundX;
                this.target.x += mapRange.minX - minBoundX;
                isCrossRange = true;
            }

            if (maxBoundY >= mapRange.maxY) {
                position.y -= maxBoundY - mapRange.maxY;
                this.target.y -= maxBoundY - mapRange.maxY;
                isCrossRange = true;
            }else if(minBoundY < mapRange.minY){
                position.y += mapRange.minY - minBoundY;
                this.target.y += mapRange.minY - minBoundY;
                isCrossRange = true;
            }

            if(isCrossRange){
                this.app.camera.lookAt(this.target);
                this.app.map.computeVisualBound(); //重新计算地图的可视边界，边界数据在加载瓦片的时候会用上
            }
        }
    }

    public setMaxPolarAngle(angle: number) {
        this.maxPolarAngle = MathUtils.degToRad(angle);
    }
    public rotateLeft(angle: number) {
        this.sphericalDelta.theta -= angle;
    }
    public rotateUp(angle: number) {
        this.sphericalDelta.phi -= angle;
    }

    protected panX(cameraPoint: Vector3, mouseDownPoint: Vector3, inverseMatrix: Matrix4) {
        let firstRow = new Vector4(inverseMatrix.elements[0], inverseMatrix.elements[4], inverseMatrix.elements[8], inverseMatrix.elements[12]);
        let originPoint = new Vector4(mouseDownPoint.x, mouseDownPoint.y, mouseDownPoint.z, 1);

        let dotValue = firstRow.dot(originPoint);
        let offsetX = (cameraPoint.x - dotValue);
        return offsetX;
    }

    protected panY(cameraPoint: Vector3, mouseDownPoint: Vector3, inverseMatrix: Matrix4) {
        let secondRow = new Vector4(inverseMatrix.elements[1], inverseMatrix.elements[5], inverseMatrix.elements[9], inverseMatrix.elements[13]);
        let originPoint = new Vector4(mouseDownPoint.x, mouseDownPoint.y, mouseDownPoint.z, 1);

        let dotValue = secondRow.dot(originPoint);
        let offsetY = (cameraPoint.y - dotValue);
        return offsetY;
    }

    protected panZ(cameraPoint: Vector3, mouseDownPoint: Vector3, inverseMatrix: Matrix4) {
        let secondRow = new Vector4(inverseMatrix.elements[2], inverseMatrix.elements[6], inverseMatrix.elements[10], inverseMatrix.elements[14]);
        let originPoint = new Vector4(mouseDownPoint.x, mouseDownPoint.y, mouseDownPoint.z, 1);

        let dotValue = secondRow.dot(originPoint);
        let offsetZ = (cameraPoint.z - dotValue);
        return offsetZ;
    }

    protected pan(isUpdateZ: boolean) {
        let inveseMatrix = this.camera.matrixWorldInverse.clone();
        let objectMatrix = this.camera.matrix;
        let dragPointInCamera = this.app.mouse.dragPointInCamera;
        let mouseDownPoint = this.app.mouse.mouseDownPoint;
        let offsetX = this.panX(dragPointInCamera, mouseDownPoint, inveseMatrix);
        let offsetY = this.panY(dragPointInCamera, mouseDownPoint, inveseMatrix);
        let offsetZ = 0;
        if (isUpdateZ) {
            offsetZ = this.panZ(dragPointInCamera, mouseDownPoint, inveseMatrix);
        }
        inveseMatrix.elements[12] += offsetX;
        inveseMatrix.elements[13] += offsetY;
        inveseMatrix.elements[14] += offsetZ;
        let newObjetMatrix = inveseMatrix.invert();
        let newPositionX = newObjetMatrix.elements[12];
        let newPositionY = newObjetMatrix.elements[13];
        let newPositionZ = newObjetMatrix.elements[14];
        this.panOffset.x = newPositionX - objectMatrix.elements[12];
        this.panOffset.y = newPositionY - objectMatrix.elements[13];
        if (isUpdateZ) {
            this.panOffset.z = newPositionZ - objectMatrix.elements[14];
        } else {
            this.panOffset.z = 0;
        }
    }
    protected dollyIn(dollyScale: number) {
        this.scale /= dollyScale;
    }
    protected dollyOut(dollyScale: number) {
        this.scale *= dollyScale;
    }
    protected down(e) {
        if (!this.enabled) return;
        switch (e.button) {
            case 2:
                this.rotateStart.set(e.pixel.getX(), e.pixel.getY());
                break;
            case 0:
                this.panStart.set(e.pixel.getX(), e.pixel.getY());
                break;
        }
    }
    /** 旋转 */
    protected rotate(e) {
        if (!this.enabled) return;

        this.rotateEnd.set(e.pixel.getX(), e.pixel.getY());
        this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart);
        let size = this.app.getSize();
        this.rotateLeft(2 * Math.PI * this.rotateDelta.x / size.getWidth() * this.rotateSpeed);
        this.rotateUp(2 * Math.PI * this.rotateDelta.y / size.getHeight() * this.rotateSpeed);
        this.rotateStart.copy(this.rotateEnd);
        this.isDragging = true;
    }

    /** 设置相机的移动量 */
    public setCameraOffset(e, isUpdateZ = false) {
        if (!this.enabled) return;

        if (this.isDragging && this.app.camera.cameraUtils.isCollisionEye) {
            return;
        }

        let mouseDownPoint = this.app.mouse.mouseDownPoint;
        let dragPointInCamera = this.app.mouse.dragPointInCamera;
        if (!mouseDownPoint || !dragPointInCamera) return;
        this.panEnd.set(e.pixel.getX(), e.pixel.getY());
        this.pan(isUpdateZ);
        this.panStart.copy(this.panEnd);
        if (!this.isDragging) {//第一次触发movestart
            this.app.fireEvent(Events.MapMoveStart);
        } else {
            this.app.fireEvent(Events.MapMove);
        }
        this.isDragging = true;
    }

    /** 缩放 */
    protected wheel(e) {
        if (!this.enabled) return;
        if (e.deltaY < 0) {
            this.dollyOut(this.getZoomScale());
        } else {
            this.dollyIn(this.getZoomScale());
        }
    }
    protected getZoomScale() {
        return Math.pow(1 /* 0.95 */, this.zoomSpeed);
    }
    protected getAutoRotationAngle() {
        return 2 * Math.PI / 60 / 60 * this.autoRotateSpeed;
    }
    /** 设置旋转角度 */
    public setRotationAngle(angle: number) {
        this.sphericalDelta.theta = MathUtils.degToRad(angle) - this.spherical.theta;
    }
    /** 设置俯视角度 */
    public setLookAngle(angle: number) {
        this.sphericalDelta.phi = MathUtils.degToRad(angle) - this.spherical.phi;
    }
    /** 中心点变化 */
    protected centerChange(position: Point, animation: boolean) {
        this.isDragging = true;
        let newtarget = position.toVector3();
        if (animation) {
            let endeye = this.camera.position.clone().add(newtarget.clone().sub(this.target.clone()));
            let endtarget = newtarget;
            this.app.camera.fly(this.camera.position, this.app.camera.lookAtTarget, endeye, endtarget);
        } else {
            this.camera.position.add(newtarget.clone().sub(this.target.clone()));
            this.app.camera.lookAt(newtarget);
        }
        this.app.options.Camera.lookAt = new Point(0, 0, 0, Const.EPSGType.EPSGWeb).fromVector3(this.app.camera.lookAtTarget);
        this.app.options.Camera.position = new Point(0, 0, 0, Const.EPSGType.EPSGWeb).fromVector3(this.camera.position);
    }

    private dragEnd(e) {

    }

    protected register() {
        this.app.on(Events.MouseDown, this.down, this);
        this.app.on(Events.MapRotate, this.rotate, this);
        this.app.on(Events.MapDragging, this.setCameraOffset, this);
        this.app.on(Events.MapDragEnd, this.dragEnd, this);
        this.app.on(Events.MouseWheel, this.wheel, this);
        this.app.on(Events.Render, this.update, this);
        this.app.on(Events.CenterChange, this.centerChange, this);
        this.app.on(Events.Dispose, this.dispose, this);
        this.app.on(Events.recoreCameraMsg, this.recordCameraMsg, this);
        this.app.on(Events.MapRange, this.limitMapRange, this);
    }
    public dispose() {
        this.app.off(Events.MouseDown, this.down, this);
        this.app.off(Events.MapRotate, this.rotate, this);
        this.app.off(Events.MapDragging, this.setCameraOffset, this);
        this.app.off(Events.MapDragEnd, this.dragEnd, this);
        this.app.off(Events.MouseWheel, this.wheel, this);
        this.app.off(Events.Render, this.update, this);
        this.app.off(Events.CenterChange, this.centerChange, this);
        this.app.off(Events.Dispose, this.dispose, this);
        this.app.off(Events.recoreCameraMsg, this.recordCameraMsg, this);
        this.app.off(Events.MapRange, this.limitMapRange, this);
    }
}
